package ua.kpi.fpm.km61.strat.sp.sa;

import java.util.Collection;
import java.util.HashSet;
import java.util.ListIterator;
import java.util.Set;

import ua.kpi.fpm.km61.strat.sp.rules.GramSymbol;
import ua.kpi.fpm.km61.strat.sp.rules.NonTerminal;
import ua.kpi.fpm.km61.strat.sp.rules.Rule;
import ua.kpi.fpm.km61.strat.sp.rules.Terminal;
import ua.kpi.fpm.km61.strat.sp.util.FileProcessor;

public class Grammar {

	private Collection<Rule> rules;
	
	private Set<Terminal> terminals;

	public Grammar() {
		this.terminals = FileProcessor.parseTerminals();
		this.rules = FileProcessor.parseRules(this.terminals);
	}

	public void first() {
		boolean end;
		do {
			end = false;
			for (Rule rule : rules) {
				NonTerminal nonterminal = rule.getIn();
				if (rule.getChain().getOut().isEmpty()) {
					end |= nonterminal.addFirst(new Terminal("Eps"));
					continue;
				}
				for (GramSymbol symbol : rule.getChain().getOut()) {
					if (symbol.getClass().equals(Terminal.class)) {
						end |= nonterminal.addFirst((Terminal) symbol);
						break;
					}
					if (((NonTerminal) symbol).getFirst().isEmpty()) {
						break;
					}
					end |= nonterminal.addFirst((NonTerminal) symbol);
					if (!((NonTerminal) symbol).isEps()) {
						break;
					}
				}
			}
		} while (end);

	}

	public void follow() {
		boolean end;
		do {
			end = false;
			for (Rule rule : rules) {
				NonTerminal nonterminal = rule.getIn();
				Set<Terminal> next = new HashSet<Terminal>();
				next.addAll(nonterminal.getFollow());
				ListIterator<GramSymbol> iter = rule.getChain().getOut()
						.listIterator(rule.getChain().getOut().size() + 1);
				while (iter.hasPrevious()) {
					GramSymbol symbol = iter.previous();
					if (symbol.getClass().equals(Terminal.class)) {
						next.clear();
						next.add((Terminal) symbol);
						continue;
					}
					NonTerminal chainNonTerm = (NonTerminal) symbol;
					end |= chainNonTerm.addFollow(next);
					if (!(chainNonTerm.isEps())) {
						next.clear();
					}
					next.addAll(chainNonTerm.getFirst());
				}
				// for (int i = rule.getChain().getOut().size(); i >= 0; i--) {
				// GramSymbol symbol = rule.getChain().getOut().get(i);
				// if (symbol.getClass().equals(Terminal.class)) {
				// next.clear();
				// next.add((Terminal) symbol);
				// continue;
				// }
				// NonTerminal chainNonTerm = (NonTerminal) symbol;
				// end |= chainNonTerm.addFollow(next);
				// if (!(chainNonTerm.isEps())) {
				// next.clear();
				// }
				// next.addAll(chainNonTerm.getFirst());
				// }
			}
		} while (end);
	}

	public Collection<Rule> getRules() {
		return rules;
	}

	public void setRules(Collection<Rule> rules) {
		this.rules = rules;
	}

	public Set<Terminal> getTerminals() {
		return terminals;
	}

	public void setTerminals(Set<Terminal> terminals) {
		this.terminals = terminals;
	}

}
